/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package ranab.server.ftp;

import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.IOException;


/**
 * We can get the ftp data connection using this class.
 * It uses either PORT or PASV command.
 * 
 * @author <a href="mailto:rana_b@yahoo.com">Rana Bhattacharyya</a>
 */
class FtpDataConnection {
    
    private FtpConfig    mConfig   = null;
    private Socket       mDataSoc  = null;
    private ServerSocket mServSoc  = null;
    
    private InetAddress  mAddress = null;
    private int          miPort   = 0;
    
    private boolean mbPort  = false;
    private boolean mbPasv  = false;
    
    
    /**
     * Constructor.
     * @param cfg ftp config object.
     */
    public FtpDataConnection(FtpConfig cfg) {
        mConfig = cfg;
    }
      
    
    /**
     * Close data socket.
     */
    public void closeDataSocket() {
        
        // close client socket if any
        if(mDataSoc != null) {
            try {
                mDataSoc.close(); 
            } 
            catch(Exception ex) {
                mConfig.getLogger().warn(ex);
            }
            mDataSoc = null;
        }
        
        // close server socket if any
        if (mServSoc != null) {
            try {
               mServSoc.close();
            }
            catch(Exception ex) {
                mConfig.getLogger().warn(ex);
            }
            mConfig.releaseDataPort(miPort);
            mServSoc = null;
        }
    }
     
     
    /**
     * Port command.
     */
    public void setPortCommand(InetAddress addr, int port) {
        
        // close old sockets if any
        closeDataSocket();
        
        // set variables
        mbPort = true;
        mbPasv = false;
        mAddress = addr;
        miPort = port;
    } 
    
    
    /**
     * Passive command. It returns the success flag.
     */
    public boolean setPasvCommand() {
        boolean bRet = false;
        
        // close old sockets if any
        closeDataSocket(); 
        
        try {    
            
            // open passive server socket and get parameters
            int port = getPassivePort();
            if(port == -1) {
                throw new Exception("No available port found for PASV connection.");
            }
            mServSoc = new ServerSocket(port, 1, mConfig.getSelfAddress());
            mAddress = mConfig.getServerAddress();
            miPort = mServSoc.getLocalPort();          

            // set different state variables
            mbPort = false;
            mbPasv = true;
            bRet = true;
        }
        catch(Exception ex) {
            mServSoc = null;
            mConfig.getLogger().warn(ex);
        }
        return bRet;
    }
     
     
    /**
     * Get client address.
     */
    public InetAddress getInetAddress() {
        return mAddress;
    }
     
     
    /**
     * Get port number.
     */
    public int getPort() {
        return miPort;
    }
     
     
    /**
     * Get the data socket. In case of error returns null.
     */
    public Socket getDataSocket() {
       
        try {
            
            // get socket depending on the selection
            if(mbPort) {
                mDataSoc = new Socket(mAddress, miPort);                  
            }
            else if(mbPasv) {
                mDataSoc = mServSoc.accept();
            }
        }
        catch(Exception ex) {
            mConfig.getLogger().warn(ex);
            mDataSoc = null;
        }
        
        return mDataSoc;
    }
    
    
    /**
     * Get the passive port. Get it from the port pool.
     */
    private int getPassivePort() {
        return mConfig.getDataPort();
    }
    
    /**
     * Dispose data connection
     */ 
    public void dispose() {
        closeDataSocket();
    }
              
}
    
